package com.zeta.demo.es.service.es.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.zeta.demo.es.common.param.EsPageParam;
import com.zeta.demo.es.dao.PostEsRepository;
import com.zeta.demo.es.model.document.PostDocument;
import com.zeta.demo.es.model.entity.Post;
import com.zeta.demo.es.model.query.PostQueryDTO;
import com.zeta.demo.es.service.es.IPostEsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightFieldParameters;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zetaframework.base.exception.ArgumentException;
import org.zetaframework.base.result.PageResult;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 文章 ElasticSearch服务实现类
 *
 * @author gcc
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PostEsServiceImpl implements IPostEsService {

    private final PostEsRepository repository;
    private final ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 分页查询
     *
     * @param param 分页参数
     * @return 分页结果
     */
    @Override
    public PageResult<PostDocument> page(EsPageParam<PostQueryDTO> param) {
        // 构建分页器
        Pageable pageable = param.buildPage();

        // 获取查询条件
        PostQueryDTO model = Optional.ofNullable(param.getModel())
                .orElse(new PostQueryDTO());

        // 构造es查询条件
        CriteriaQuery query = new CriteriaQuery(model.buildCriteria(), pageable);

        // 分页查询
        SearchHits<PostDocument> searchResult = null;
        try {
            searchResult = elasticsearchTemplate.search(query, PostDocument.class);
        } catch (Exception e) {
            log.error("分页查询ES数据异常：{}", e.getMessage(), e);
            throw new ArgumentException("查询失败，请检查请求参数");
        }

        // 构造返回结果
        List<PostDocument> records = searchResult.stream()
                .map(SearchHit::getContent).toList();
        return new PageResult<>(records, searchResult.getTotalHits());
    }

    /**
     * 根据id查文章
     *
     * @param id 文章ID
     * @return 文章数据
     */
    @Override
    public PostDocument getById(Long id) {
        return repository.findById(id).orElse(null);
    }

    /**
     * 根据标题搜索文章(分词查询)
     *
     * @param title 标题
     * @return 文章数据列表
     */
    @Override
    public List<PostDocument> searchByTitle(String title) {
        return repository.findByTitle(title);
    }

    /**
     * 根据标题搜索文章(精确查询)
     *
     * @param keyword 关键字
     * @return 文章数据列表
     */
    @Override
    public List<PostDocument> searchByTitleKeyword(String keyword) {
        return repository.findByTitleKeyword(keyword);
    }

    /**
     * 根据内容搜索文章(高亮查询)
     *
     * @param content 文章内容
     * @return 搜索结果
     */
    @Override
    public List<String> searchByContentWithHighLight(String content) {
        if (StrUtil.isBlank(content)) {
            return Collections.emptyList();
        }

        // 创建高亮字段参数
        HighlightFieldParameters parameters = HighlightFieldParameters.builder()
                .withPreTags("<em>")
                .withPostTags("</em>")
                .build();
        // 创建高亮字段
        HighlightField highlightField = new HighlightField("content", parameters);
        // 创建高亮配置
        Highlight highlight = new Highlight(List.of(highlightField));
        // 创建高亮查询
        HighlightQuery highlightQuery = new HighlightQuery(highlight, PostDocument.class);

        // 构造es查询条件
        NativeQuery searchQuery = NativeQuery.builder()
                // 分词搜索
                .withQuery(query -> query.match(match -> match.field("content").query(content)))
                // 高亮搜索
                .withHighlightQuery(highlightQuery)
                .build();

        // 搜索
        SearchHits<PostDocument> searchHits = null;
        try {
            searchHits = elasticsearchTemplate.search(searchQuery, PostDocument.class);
        } catch (Exception e) {
            log.error("高亮查询ES数据异常：{}", e.getMessage(), e);
            throw new ArgumentException("查询失败，请检查请求参数");
        }

        // 处理结果 返回高亮结果
        return searchHits.stream()
                .map(it -> {
                    // 获取高亮结果
                    Map<String, List<String>> highlightFields = it.getHighlightFields();
                    if (!highlightFields.containsKey("content")) {
                        // 如果没有高亮结果，则返回文章内容
                        PostDocument doc = it.getContent();
                        return doc.getContent();
                    }

                    // 返回高亮结果
                    List<String> highlights = highlightFields.get("content");
                    return StrUtil.join("<br>", highlights);
                }).toList();
    }

    /**
     * 保存或修改文章
     *
     * @param post 文章数据
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveOrUpdatePost(Post post) {
        PostDocument bean = BeanUtil.toBean(post, PostDocument.class);
        try {
            repository.save(bean);
        } catch (Exception e) {
            log.error("保存or修改ES数据异常：{}", e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 删除文章
     *
     * @param id 文章ID
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeById(Long id) {
        try {
            repository.deleteById(id);
        } catch (Exception e) {
            log.error("删除ES数据异常：{}", e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 批量删除文章
     *
     * @param ids 文章ID列表
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeByIds(List<Long> ids) {
        try {
            repository.deleteAllById(ids);
        } catch (Exception e) {
            log.error("批量删除ES数据异常：{}", e.getMessage());
            return false;
        }
        return true;
    }
}
